package org.tomcat.unit501;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class HttpProcessor implements Runnable {

    private int id;

    private String threadName;

    private Thread thread = null;

    private HttpConnector connector;

    private HttpRequest request;

    private HttpResponse response;

    private boolean started= false;

    private boolean stopped;

    protected boolean available = false;

    private Socket socket;

    private boolean ok = true;

    private boolean finishResponse = true;

    private Object threadSync = new Object();

    public HttpProcessor(HttpConnector connector, int id) {
        this.connector = connector;
        this.id = id;
        this.threadName ="HttpProcessor[" + connector.getPort() + "][" + id + "]";
    }

    public void process(Socket socket) {
        InputStream input = null;
        OutputStream output = null;
        try {
            input = socket.getInputStream();
            output = socket.getOutputStream();
            request = new HttpRequest(input);
            response = new HttpResponse(output);
            response.setRequest(request);
            BufferedReader inputReader = new BufferedReader(new InputStreamReader(input));
            parseConnection(socket);
            parseRequest(inputReader);
            parseHeaders(inputReader);
            this.connector.getContainer().invoke(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (!stopped) {
            Socket socket = await();
            if (socket == null) {
                continue;
            }

            try {
                process(socket);
            } catch (Throwable t) {
                t.printStackTrace();
            }

            connector.recycle(this);
        }

        synchronized (threadSync) {
            threadSync.notifyAll();
        }
    }

    private synchronized Socket await() {
        while (!this.available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        Socket socket = this.socket;
        this.available = false;
        notifyAll();

        return (socket);
    }

    synchronized void assign(Socket socket) {
        while (this.available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.socket = socket;
        this.available = true;
        notifyAll();
    }

    private void parseConnection(Socket socket) throws IOException, ServletException {
        // TODO 没有找到对应的字段
        InetAddress address = socket.getInetAddress();
        System.out.println("request http address " +address.getHostAddress() +",port " +this.connector.getPort());
        request.setRemoteAddress(address.getHostAddress());
    }

    private void parseRequest(BufferedReader reader)
            throws IOException, ServletException {
        String requestLine = reader.readLine();
        System.out.println("parse request " +requestLine);
        String[] requests = requestLine.split("\\s+");
        // TODO 使用 BufferdReader 来完成相同的逻辑
        String method = requests[0];
        String uri = requests[1];
        String protocol = requests[2];
        if (method.length() < 1) {
            throw new ServletException("Missing HTTP request method");
        } else if (uri.length() < 1) {
            throw new ServletException("Missing HTTP request URI");
        }

        int question = uri.indexOf("?");
        if (question >= 0) {
            request.setQueryString(uri.substring(question + 1));
            uri = uri.substring(0, question);
        } else {
            request.setQueryString(null);
        }

        if (!uri.startsWith("/")) {
            int pos = uri.indexOf("://");
            if (pos != -1) {
                uri = "";
            } else {
                uri = uri.substring(pos);
            }
        }

        String match = ";jsessionid";
        int semicolon = uri.indexOf(match);
        if (semicolon >= 0) {
            String rest = uri.substring(semicolon + match.length());
            int semicolon2 = rest.indexOf(";");
            if (semicolon2 >= 0) {
                request.setRequestedSessionId(rest.substring(0, semicolon2));
            } else {
                request.setRequestedSessionId(rest);
                rest = "";
            }
            request.setRequestedSessionURL(true);
            uri = uri.substring(0, semicolon) + rest;
        } else {
            request.setRequestedSessionId(null);
            request.setRequestedSessionURL(false);
        }

        String normalizedUri = normalize(uri);
        ((HttpRequest) request).setMethod(method);
        request.setProtocol(protocol);
        if (normalizedUri != null) {
            ((HttpRequest) request).setRequestURI(normalizedUri);
        } else {
            ((HttpRequest) request).setRequestURI(uri);
        }
        if (normalizedUri == null) {
            throw new ServletException("Invalid URI" + uri +",");
        }
    }

    public void parseHeaders(BufferedReader reader)
            throws IOException, ServletException {
        while (true) {
            String headerLine = reader.readLine();
            System.out.println("line value " +headerLine);
            String[] headers = headerLine.split(":");
            if (headerLine == null || headerLine.length() == 0) {
                break;
            } else {
                request.addHeader(headers[0].trim(), headers[1].trim());
            }
        }
    }

    /**
     * 对 uri 进行修正
     * @param uri
     * @return
     */
    public String normalize(String uri) {
        // TODO 缺少相应的逻辑
        return uri;
    }

    private void threadStart() {
        thread = new Thread(this, this.threadName);
        thread.setDaemon(true);
        thread.start();
    }

    public void start() {

        if (started)
           return;
        started = true;

        threadStart();

    }

}
